from django.shortcuts import render

# Create your views here.
from rest_framework.response import Response

from goods.models import SKU

"""
0.通过前端传递过来的token进行判断用户有没有登录

    request.user 还需要判断用户的登录状态

1.登录用户保存在后端redis
    未登录用户保存在cookie

2.cookie数据和后端保存的信息有:商品id sku_id 数量count 选中状态selected

3.redis保存的时候数据类型的选取
    redis的数据是保存在内存中的,原则是 占用尽量少的数据
    因为我们每次去判断商品是否选中时,都要使用代码去判断,
    这个就是用代码换取redis的空间
    sku_id  count  selected
hash         hash_key:key:value
             cart_user:  sku_id:count
                     selected_sku_id:True
             cart_1:  1:3
                    2:4
记录 选中的状态
记录选中的商品id 我们用集合
set         set_key: value2,value5,value1,...
{1,2}

4. cookie数据的 数据格式
   {
        sku_id: {count:xxxx,selected:xxx}
        1:{count:5,selected:True}
        5:{count:50,selected:True}
        9:{count:1,selected:False}
   }
   base42
   因为base64只能处理二进制,所以我们需要将字典转换为二进制
   dict --> pickle(转换为二进制dumps) -->base64.b64encode(base64.b64encode.decode)
    base64.b64decode()-->pickle(loads) -->dict
     base64
    1G=1024M
    1M=1024KB
    1KB = 1024B
    1B = 8bytes
    bytes 是计算机的最小单位 其实它就是 0 1
    8bytes

    0100  0001  0100  0001  0100  0001
        A           A           A

    010000      010100     000101        000001
        x           y       z               n

"""
"""
添加购物车的逻辑
当用户点击添加购物车的时候,前端应发送一个ajax请求,包含sku_id,count,selected(可选),token(可选)
1.接收数据,校验数据
2.获取对应的数据
3.判断用户的登录状态
4.登录用户redis
    4.1连接reids
    4.2保存数据
    4.3返回响应
5.未登录用户cookie
    5.1先读取cookie数据,判断数据是否存在
    cart = request.COOKIES.get('cart')
    cart = gAN9cQBLAX1xAShYCAAAAHNlbGVjdGVkcQKIWAUAAABjb3VudHEDSwV1cy4=
    5.2更新购物车数据

    dict = {}
    5.3我们需要对字典进行处理( dict --> pickle.dumps -->base64.encode().decode())
    response.set_cookie('cart','gAN9cQBLAX1xAShYCAAAAHNlbGVjdGVkcQKIWAUAAABjb3VudHEDSwV1cy4=')
    5.4返回响应

    POST  cart/

"""
from rest_framework.views import APIView
from.serializers import CartSerializer,CartSKUSerializer,CartDeleteSerializer
from django_redis import get_redis_connection
import base64
import pickle
class CartView(APIView):

    """
    我们的视图 如果你传递了token,会调用视图的perform_authentication 方法
    来验证一下用户的信息
    因为用户的token过期了,或者用户token是伪造的用户就不能添加到购物车了
    应该先让用户添加到购物车,也就是先不让视图调用perform_authentication

    """
    def perform_authentication(self, request):
        pass

    def post(self,request):

        # 1.接收数据,校验数据
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 2.获取对应的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')
        # 3.判断用户的登录状态
        try:
            user = request.user
        except Exception:
            user = None
        # 判断用户是否是登录状态我们是根据 user的一个是属性
        # is_authenticated 如果为认证用户,则返回True
        # is_authenticated 如果不为认证用户,则返回False
        # request.user.is_authenticated
        if user is not None and user.is_authenticated:

            # 4.登录用户redis
            #     4.1连接reids
            redis_conn = get_redis_connection('cart')
            #     4.2保存数据

            #hash
            # redis_conn.hset('cart_%s'%user.id,sku_id,count)
            # redis_conn.hincrby('cart_%s'%user.id,sku_id,count)
            #set
            # if selected:
            #     redis_conn.sadd('cart_selected_%s'%user.id,sku_id)

            #创建管道
            pipe = redis_conn.pipeline()
            #hash
            pipe.hincrby('cart_%s' % user.id, sku_id, count)
            #set
            if selected:
                pipe.sadd('cart_selected_%s'%user.id,sku_id)

            #管道统一执行
            pipe.execute()

            #     4.3返回响应
            return Response(serializer.data)

        else:

            # 5.未登录用户cookie
            #     5.1先读取cookie数据,判断数据是否存在
            #     cart = request.COOKIES.get('cart')
            #     cart = gAN9cQBLAX1xAShYCAAAAHNlbGVjdGVkcQKIWAUAAABjb3VudHEDSwV1cy4=
            cart_str = request.COOKIES.get('cart')

            if cart_str is not None:
                #有数据
                # cart_str 先 进行  base64的解码
                decode = base64.b64decode(cart_str)
                #再进行将二进制转换为字典
                cart = pickle.loads(decode)
            else:
                #没有数据
                cart = {}
            # cart = {sku_id:{count:xxx,selected:xxxx}}
            # cart = {1:{count:2,selected:True}}

            #     5.2更新购物车数据
            # #      我们需要判断 sku_id 是否在 cart中
            # 如果在cart中,我们需要对商品的count进行累加
            # 如果没有在cart中,我们添加对应的数据就可以了
            if sku_id in cart:
                # 原商品个数
                # {1:{count:2,selected:True}}
                orginal_count = cart[sku_id]['count']
                #累加
                count += orginal_count

            cart[sku_id] = {
                'count':count,
                'selected':selected
            }

            #     dict = {}
            #     5.3我们需要对字典进行处理( dict --> pickle.dumps -->base64.encode().decode())
            #     response.set_cookie('cart','gAN9cQBLAX1xAShYCAAAAHNlbGVjdGVkcQKIWAUAAABjb3VudHEDSwV1cy4=')

            # 将 cart这个字典转换为 二进制
            dumps = pickle.dumps(cart)
            # 再将二进制进行base64编码
            encode = base64.b64encode(dumps)

            # encode 是一个二进制 再进行 decode() 转换为字符串
            new_cart = encode.decode()

            #     5.4返回响应
            response = Response(serializer.data)
            response.set_cookie('cart',new_cart)
            return response

    """
    当用户点击购物车列表的时候,我们的前端应该发送一个ajax请求 jwt token请求头中

    1.获取用户信息,根据用户信息去判断用户登录状态
    2.登录用户存到redis
        2.1连接redis
        2.2获取数据hash set
        2.3根据数据的id查询商品信息
        2.4对数据进行序列化操作
        2.5返回响应
    3.未登录用户存到cookie
        3.1从cookie中获取数据,判断数据是否存在
        3.2如果存在数据,我们根据数据id查询商品信息
        3.3对数据进行序列化操作
        3.4返回响应

        1.获取用户信息,根据用户信息判断登录状态
        2.登录用户存到redis
            2.1连接redis
            2.2获取数据hash set
        3.未登录用户存到cookie
            3.1从cookie中获取数据,判断数据是否存在
        4.如果存在数据,根据数据id查询商品信息
        5.对数据进行序列化操作
        6.返回响应
        GET  /cart/

    """
    def get(self,request):

        # 1.获取用户信息,根据用户信息判断登录状态
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 2.登录用户存到redis
            #     2.1连接redis
            redis_conn = get_redis_connection('cart')
            #     2.2获取数据hash set
            #hash
            sku_count = redis_conn.hgetall('cart_%s'%user.id)
            # {1:4,2:20,3:40}
            #set 选中的id
            selected_ids = redis_conn.smembers('cart_selected_%s'%user.id)
            # [1,2,3....]
            #因为redis中的数据和cookie中的数据格式不一致,所以进行序列化操作的时候不统一
            #我们将redis的数据转成cookie的格式
            # cart={1:{count:xxx,selected:xxxx},2:{count:xxx,selected:xxxx}}

            cart = {}

            #遍历hash数据;
            for sku_id,count in sku_count.items():

                if sku_id in selected_ids:
                    selected = True
                else:
                    selected = False
                #redis的数据类型是bytes转换一下
                cart[int(sku_id)]={
                    'count':int(count),
                    # 'selected': sku_id in selected_ids
                    'selected':selected
                }
        else:
            # 3.未登录用户存到cookie
            #     3.1从cookie中获取数据,判断数据是否存在
            cookie_str = request.COOKIES.get('cart')

            if cookie_str is not None:
                #有数据
                #先经过base64解码
                decode = base64.b64decode(cookie_str)
                #再转为字典
                cart = pickle.loads(decode)

            else:
                #没有数据
                cart = {}
        # cart={1:{count:xxx,selected:xxxx},2:{count:xxx,selected:xxxx}}
        ids = cart.keys()

        # 4.如果存在数据,根据数据id查询商品信息
        skus = SKU.objects.filter(pk__in = ids)

        for sku in skus:
            sku.count = cart[sku.id]['count']
            sku.selected = cart[sku.id]['selected']

        # 5.对数据进行序列化操作

        serializer = CartSKUSerializer(skus,many=True)

        # 6.返回响应
        # 序列化操作的时候 , 序列化操作的原理是: 根据 序列化器的字段去获取模型中数据
        # 现在模型中没有 count和selected
        return Response(serializer.data)

    """
    修改购物车的业务逻辑
    当用户修改数量/选中状态的时候,前端应将sku_id,count,selected jwt token(请求头中)
    提交给后端
    1.接收参数,校验参数
    2.获取数据sku_id,count,selected
    3.获取用户登录状态,根据用户登录进行判断
    4.登录用户从redis获取
        4.1连接redis
        4.2修改数据 hash set
        4.3返回响应(必须包含商品的数量)
    5.未登录用户从cookie中获取
        5.1从cookie中获取数据,判断数据是否存在
        5.2修改数据
        5.3cart 我们需要对字典进行base64处理
        5.4返回响应(必须包含商品的数量)
    """
    def put(self,request):

        # 1.接收参数,校验参数
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 2.获取数据sku_id,count,selected
        sku_id = serializer.data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')

        # 3.获取用户登录状态,根据用户登录进行判断
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:

            # 4.登录用户从redis获取
            #     4.1连接redis
            redis_conn = get_redis_connection('cart')
            #     4.2修改数据 hash set
            #hash
            redis_conn.hset('cart_%s'%user.id,sku_id,count)
            #set
            if selected:
                #选中
                redis_conn.sadd('cart_selected_%s'%user.id,sku_id)
            else:
                #没选中
                redis_conn.srem('cart_selected_%s'%user.id,sku_id)

            #     4.3返回响应(必须包含商品的数量)
            return Response(serializer.data)
        else:
            # 5.未登录用户从cookie中获取
            #     5.1从cookie中获取数据,判断数据是否存在
            cookie_str = request.COOKIES.get('cart')

            if cookie_str is not None:
                cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                cart ={}
            #     5.2修改数据
            if sku_id in cart:
                cart[sku_id] = {
                    'count':count,
                    'selected':selected
                }

            # 5.3cart 我们需要对字典进行base64处理
            new_cookie = base64.b64encode(pickle.dumps(cart)).decode()
            #     5.4返回响应(必须包含商品的数量)
            response = Response(serializer.data)

            response.set_cookie('cart',new_cookie)
            return response
    """
    当用户点击删除按钮的时候,前端需要商品id传递给后端
    1.接收sku_id,校验
    2.获取sku_id
    3.获取用户状态
    4.登录用户 redis
        4.1连接redis
        4.2删除数据hash set
        4.3返回响应
    5.未登录用户 cookie
        5.1获取cookie数据
        5.2删除数据
        5.3对cart进行处理
        5.4返回响应

    """
    def delete(self,request):

        # 1.接收sku_id,校验
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 2.获取sku_id
        sku_id = serializer.data.get('sku_id')

        # 3.获取用户状态
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            # 4.登录用户 redis
            #     4.1连接redis
            redis_conn = get_redis_connection('cart')
            #     4.2删除数据hash set
            #hash
            redis_conn.hdel('cart_%s'%user.id,sku_id)
            #set
            redis_conn.srem('cart_selected_%s'%user.id,sku_id)

            #     4.3返回响应
            return Response(serializer.data)
        else:
            # 5.未登录用户 cookie
            #     5.1获取cookie数据
            cookie_str = request.COOKIES.get('cart')
            if cookie_str is not None:
                cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                cart = {}

            #     5.2删除数据
            if sku_id in cart:
                del cart[sku_id]
            #     5.3对cart进行处理
            new_cart = base64.b64encode(pickle.dumps(cart)).decode()
            #     5.4返回响应
            response = Response(serializer.data)

            response.set_cookie('cart',new_cart)

            return response















